#include "../glutil/GLee.h"

#include <QFutureWatcher>
#include <QGLWidget>
#include <QtConcurrentMap>
#include <QTime>
#include <QString>
#include <QLabel>

#include <math.h>
#include <bitset>
#include "../AOProgressDialog.h"
#include "../geom/Ray.h"
#include "../geom/Triangle.h"
#include "POVObject.h"
#include "POVMesh.h"


using namespace POVGeom;

namespace 
{
	class PointsOcclusionProcessor : public std::unary_function<const MeshPoint&,void>
	{
		private:
			Octree<Triangle> * octree;
			int numRays;
			PolyMesh * m_mesh;

		public:
			PointsOcclusionProcessor(Octree<Triangle> * oct, int numRaysPerVertex, PolyMesh * mesh):octree(oct),numRays(numRaysPerVertex), m_mesh(mesh)
			{
			}

			void operator()(MeshPoint & p)
			{
				Vector3D currentRay;

				int occludedRays=0;
				Vector3D finalNormal;
				for(int k=0;k<numRays;++k)
				{
					currentRay = Vector3D::hemiRandom(p.n);
					Ray r( p.p+(p.n/50.0) , currentRay);
					if(octree->findIfIntersection(r))	
						++occludedRays;
					else
						finalNormal+=currentRay;
				}

				//p.n = finalNormal.getNormalized(); // We set the bent normal
				p.AOFactor = 1-(occludedRays/(float)numRays);
			}
	};
}

namespace
{
	class mySleep : public QThread
	{
		public:
			static void msleep(unsigned long msecs)
			{
				QThread::msleep(msecs);
			}
	};
}

void POVMesh::stopAOProcessing()
{
	mustStopAOProcessing = true;
}

void POVMesh::generateAmbientOcclusion(Octree<Triangle> * octree, int numRays,AOProgressDialog * progress, int thisMeshNum)
{
	connect(this,SIGNAL(aoPointsProcessed(int,int)),progress,SLOT(updateProcessedPoints(int,int)));
	connect(this,SIGNAL(meshFinished(int)),progress,SLOT(meshFinished(int)));
	connect(progress,SIGNAL(stopAOProcessing()),this,SLOT(stopAOProcessing()));

	if(numRays == 0 || progress->mustStopProcessing())
	{
		emit meshFinished(thisMeshNum);
		return;
	}
	PointsOcclusionProcessor pointProcessor(octree,numRays, m_mesh);
	
	QFuture<void> future = QtConcurrent::map(m_mesh->points().begin(),m_mesh->points().end(), pointProcessor);

	mustStopAOProcessing = false;
	while(!mustStopAOProcessing && future.progressValue()<future.progressMaximum())
	{
		int nowValue = future.progressValue();
		emit aoPointsProcessed(thisMeshNum, nowValue);
		mySleep::msleep(100);
	}
	if(mustStopAOProcessing)
		future.cancel();
	printf("SALIENDO DEL BUCLE\n");
	future.waitForFinished();
	printf("FUTURE FINALIZADO\n");
	emit meshFinished(thisMeshNum);
}

